﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Net.Sockets;
using System.Net;
using System.IO;

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        ComParser commander = new ComParser();
        ComParser deserializer = new ComParser();
        Timer t = new Timer();
        List<AgentLink> netnodeAgents = new List<AgentLink>();
        List<AgentLink> clientAgents = new List<AgentLink>();
        List<object> somelist = new List<object>();

        public Form1()
            {
            InitializeComponent();
            InitializeCommands();
            Deserialize();
            RefreshListBox(listBox1, netnodeAgents);
            RefreshListBox(listBox2, clientAgents);
            StartTimer();
            }
        private void InitializeCommands()
            {
            commander.AddFormat("GET FORWARD [inport]:[invpi]:[invci] IN [node]", GetLinkRequest);
            commander.AddFormat("FORWARD [inport]:[invpi]:[invci] TO [outport]:[outvpi]:[outvci] IN [node]", LinkRequest);
            commander.AddFormat("REMOVE FORWARD [inport]:[invpi]:[invci] IN [node]", UnlinkRequest);
            commander.AddFormat("KEEPALIVE [agent]", KeepNodeAlive);
            commander.AddFormat("AUTHENTICATE [agent]", AuthenticateAgent);

            deserializer.AddFormat("CLIENT [name] LISTENS [inip]:[inport] SENDS [outip]:[outport]", AddClient);
            deserializer.AddFormat("NETNODE [name] LISTENS [inip]:[inport] SENDS [outip]:[outport]", AddNetnode);
            }
        //WCZYTYWANIE DANYCH Z PLIKU
        private void Deserialize()
            {
            List<string> config = new List<string>();
            string line = "";
            StreamReader objReader = new StreamReader("config.txt"); //args[1]
            while (line != null)
            {
                line = objReader.ReadLine();
                if (line != null)
                    config.Add(line);

            }
            objReader.Close();

            foreach (string s in config)
                {
                deserializer.Execute(s);
                }
            }
        private void AddClient(ArgList args)
            {
            clientAgents.Add( new AgentLink(this, args.Get("name"),
                new IPEndPoint(IPAddress.Parse(args.Get("inip")), Int32.Parse(args.Get("inport"))),
                new IPEndPoint(IPAddress.Parse(args.Get("outip")), Int32.Parse(args.Get("outport"))),
                true) );

            }
        private void AddNetnode(ArgList args)
            {
            netnodeAgents.Add(new AgentLink(this, args.Get("name"),
                new IPEndPoint(IPAddress.Parse(args.Get("inip")), Int32.Parse(args.Get("inport"))),
                new IPEndPoint(IPAddress.Parse(args.Get("outip")), Int32.Parse(args.Get("outport"))),
                false));
            }
        private void RefreshListBox(ListBox lb, List<AgentLink> ds) 
            {
                lb.DataSource = null;
                lb.DataSource = ds;
                lb.DisplayMember = "DispName";
            }
        //DZIAŁANIA ZWIĄZANE Z WĘZŁAMI
        public void NodeIsKilled(AgentLink agent, bool doPrint)
            {
            if (agent.GetIsClient())
                {
                RefreshListBox(listBox2, clientAgents);
                if (doPrint) Print("+ Z przykrością zwiadamiamy, że węzeł kliencki " + agent.gName + " nie daje znaków życia!");
                }
            else
                {
                RefreshListBox(listBox1, netnodeAgents);
                if (doPrint) Print("+ Z przykrością zwiadamiamy, że węzeł sieciowy " + agent.gName + " nie daje znaków życia!");
                }
            }

        public void NodeIsBorn(AgentLink agent)
        {
            if (agent.GetIsClient())
            {
                Print("♥ Węzeł kliencki " + agent.gName + " gotowy do pracy!");
                RefreshListBox(listBox2, clientAgents);
            }
            else
                {
                Print("♥ Węzeł sieciowy " + agent.gName + " gotowy do pracy!");
                RefreshListBox(listBox1, netnodeAgents);
                }
        }

        public void KeepNodeAlive(ArgList args)
            {
            //SendToAgent(args.Get("agent"), "NOTIFY KEEPALIVE");
            }
        public void AuthenticateAgent(ArgList args)
            {
            SendToAgent(args.Get("agent"), "GET WHOIS", true);
            }
        public void SendToAgent(string name, string command, bool canClient)
            {
            Print("► Wysyłanie polecenia \""+command+"\" do agenta "+name+"!");
            foreach (AgentLink agent in netnodeAgents)
                {
                if (agent.uName == name)
                    {
                    agent.SendRequest(command);
                    return;
                    }
                }
            if (!canClient) {
                Print("‼ Nie znaleziono węzła sieciowego "+name+"!");
                return;
                }
            foreach (AgentLink agent in clientAgents)
                {
                if (agent.uName == name)
                    {
                    agent.SendRequest(command);
                    return;
                    }
                }
            Print("‼ Nie znaleziono węzła "+name+"!");
            }
        //OBSŁUGA LICZNIKA
        private void StartTimer()
            {
            t.Tick += new EventHandler(DoTick);
            t.Interval = 1000;
            t.Start();
            }

        private void DoTick(object sender, EventArgs e)
            {
            foreach (AgentLink aLink in netnodeAgents)
                {
                aLink.CountDown();
                }
            foreach (AgentLink aLink in clientAgents)
            {
                aLink.CountDown();
            }
            }
        //ZDARZENIA
        private void button1_Click(object sender, EventArgs e)
        {
            if (!commander.Execute(textBox2.Text))
                PrintDef();
        }

        public void Print(string toPrint)
        {
            textBox1.AppendText(toPrint + "\n");
        }

        //ODPOWIEDZI
        public void GetLinkRequest(ArgList args)
        {
            SendToAgent(args.Get("node"), "GET FORWARD:"
                    + args.Get("inport") + "," + args.Get("invpi") + "," + args.Get("invci"),
                    false);
            textBox2.Clear();
        }
        public void LinkRequest(ArgList args)
            {
            SendToAgent(args.Get("node"), "SET FORWARD:"
                    + args.Get("inport") + "," + args.Get("invpi") + "," + args.Get("invci")
                     + "," + args.Get("outport") + "," + args.Get("outvpi") + "," + args.Get("outvci"),
                    false);
            textBox2.Clear();
            }
        public void UnlinkRequest(ArgList args)
        {
            SendToAgent(args.Get("node"), "SET REMOVE:"
                    + args.Get("inport") + "," + args.Get("invpi") + "," + args.Get("invci"),
                    false);
            textBox2.Clear();
        }
        public void PrintDef()
            {
            Print("Invalid command!");
            }
    }

    //WSZYStKIE KLASY POTRZEBNE DO PARSOWANIA KOMEND!
    public delegate void WrittenMethod(ArgList args);
    public class ComParser
    {
        ArgList args = new ArgList();
        List<ComFormat> coms = new List<ComFormat>();

        public void AddFormat(string mask, WrittenMethod method)    { coms.Add(new ComFormat(mask, method)); }
        public Boolean Match(string toCheck, string toComp)
        {
            string checkRem, compRem, compBit;
            int obi, cbi, biti;
            checkRem = toCheck; compRem = toComp;

            obi = compRem.IndexOf('['); cbi = compRem.IndexOf(']');
            while (compRem != "")
            {
                obi = compRem.IndexOf('['); cbi = compRem.IndexOf(']');
                if (obi > -1 && cbi > -1)
                {
                    compBit = compRem.Substring(0, obi);
                    compRem = compRem.Substring(Math.Min(compRem.Length, cbi + 1));
                }
                else
                {
                    compBit = compRem;
                    compRem = "";
                }
                biti = checkRem.IndexOf(compBit);
                if (biti == -1)
                    return false;
                checkRem = checkRem.Substring(biti + compBit.Length);
            }
            return true;
        }

        public Boolean Execute(string toExecute)
            {
                foreach (ComFormat format in coms)
                    {
                        if (ExecuteSingle(toExecute, format.gMask(), format.gMethod())) return true;
                    }
                return false;
            }
        public Boolean ExecuteSingle(string toExecute, string mask, WrittenMethod called)
        {
            args.Clear();
            string exRem, maskRem, maskBit;
            string prevName, curName;
            int obi, cbi, biti;
            bool first = true;
            exRem = toExecute; maskRem = mask;

            prevName = "";
            curName = "";

            biti = 0;

            obi = maskRem.IndexOf('['); cbi = maskRem.IndexOf(']');
            while (maskRem != "")
            {
                obi = maskRem.IndexOf('['); cbi = maskRem.IndexOf(']');
                if (obi > -1 && cbi > -1)
                {
                    maskBit = maskRem.Substring(0, obi);
                    curName = maskRem.Substring(obi+1, cbi-(obi+1));
                    maskRem = maskRem.Substring(Math.Min(maskRem.Length, cbi + 1));
                }
                else
                {
                    maskBit = maskRem;
                    curName = null;
                    maskRem = "";
                }
                biti = exRem.IndexOf(maskBit);
                if (biti == -1) return false;
                if (first && biti != 0) return false;
                if (!first) args.Add(prevName, exRem.Substring(0, biti).Trim());
                exRem = exRem.Substring(biti + maskBit.Length);

                first = false;
                prevName = curName;
            }
            if (prevName != null) args.Add(prevName, exRem);

            called(args);

            return true;
        }
    }
    public class ArgBit
    {
        public string name;
        public string value;
        public ArgBit(string newName, string newValue)
        {
            name = newName;
            value = newValue;
        }
    }
    public class ArgList
    {
        List<ArgBit> content = new List<ArgBit>();
        public void Clear()
        {
            content.Clear();
        }
        public void Add(string name, string value)
        {
            content.Add(new ArgBit(name, value));
        }
        public string Get(string key)
        {
            foreach (ArgBit arg in content)
            {
                if (arg.name == key)
                    return arg.value;
            }
            return null;
        }
    }
    public class ComFormat
    {
        string mask;
        WrittenMethod method;
        public ComFormat(string newMask, WrittenMethod newMethod)
        {
            mask = newMask;
            method = newMethod;
        }
        public string gMask() { return mask; }
        public WrittenMethod gMethod() { return method; }
    }

    //KLASA NASŁUCHUJĄCA AGENTÓW!
    public class AgentLink
    {
        private string name;
        TcpListener listener;
        Timer lt;
        Byte[] listenBuffer = new Byte[256];

        TcpClient speaker;
        IPEndPoint sipep;

        bool isClient;
        int timeout;
        bool alive = false;

        ComParser interpreter = new ComParser();
        Form1 manager = null;

        //ROZPOCZYNANIE DZIAŁANIA
        public AgentLink(Form1 theManager, string newName, IPEndPoint toListen, IPEndPoint toSend, bool client)
        {
            name = newName;

            listener = new TcpListener(toListen);
            listener.Start();
            lt = new Timer();
            lt.Tick += new EventHandler(DoListen);
            lt.Interval = 50;
            lt.Start();

            sipep = toSend;
            isClient = client;
            manager = theManager;
            InitializeCommands();

            timeout = 60;
        }
        private void InitializeCommands()
        {
            interpreter.AddFormat("GETRSP WHOIS:[name]", ConfirmIdentity);
            interpreter.AddFormat("GETRSP FORWARD:[inport],[invpi],[invci],[outport],[outvpi],[outvci]", GetForwardResponse); 
            interpreter.AddFormat("SETRSP FORWARD:[ok?],[info]", ForwardResponse);
            interpreter.AddFormat("SETRSP REMOVE:[ok?],[info]", RemoveResponse);
            interpreter.AddFormat("NOTIFY KEEPALIVE:[name]", ConfirmedKeepAlive);
        }
        private void DoListen(object sender, EventArgs e)
        {
            //manager.Print(name + " listens");
            if (listener.Pending())
            {
                TcpClient sending = listener.AcceptTcpClient();
                NetworkStream s = sending.GetStream();

                int count = s.Read(listenBuffer, 0, listenBuffer.Length);
                Call(Encoding.UTF8.GetString(listenBuffer, 0, count));
            }
        }
        public void SendRequest(string request)
        {
            speaker = new TcpClient();
            speaker.SendBufferSize = 256;
            speaker.SendTimeout = 1000;
            speaker.ReceiveTimeout = 1000;

            NetworkStream s = null;
            bool uncaught = true;
            try
            {
                speaker.Connect(sipep);
                s = speaker.GetStream();
                Byte[] sendBytes = Encoding.UTF8.GetBytes(request);
                s.Write(sendBytes, 0, sendBytes.Length);
            }
            catch (SocketException)
            {
                manager.Print("‼ Nie udało się nawiązać połączenia z agentem " + name + "!");
                uncaught = false;
            }

            if (uncaught)
            {
                try
                {
                    Byte[] receiveBytes = new Byte[256];
                    int count = s.Read(receiveBytes, 0, receiveBytes.Length);
                    Call(Encoding.UTF8.GetString(receiveBytes, 0, count));
                }
                catch (IOException)
                {
                    manager.Print("‼ Nie otrzymano odpowiedzi od agenta węzła " + name + " po zadanym czasie!");
                }
            }
            speaker.Close();
        }
        public void Call(string command)
        {
            interpreter.Execute(command);
        }
        private void ConfirmIdentity(ArgList args)
        {
            if (args.Get("name") == name)
            {
                manager.Print("☺ Potwierdzono tożsamość węzła " + args.Get("name") + "!");
                KeepAlive(args);
            }
            else
            {
                manager.Print("‼ Agent węzła " + args.Get("name") + " nasłuchuje na porcie węzła " + name + "!");
            }
        }
        private void GetForwardResponse(ArgList args)
        {
            if (args.Get("inport") != "-")
            {
            manager.Print("► Węzeł "+name+" przekierowuje z portu "
                +args.Get("inport")+", VPI "+args.Get("invpi")+", VCI "+args.Get("invci")+" do portu "
                +args.Get("outport")+", VPI "+args.Get("outvpi")+", VCI "+args.Get("outvci"));
            }
            else
            {
            manager.Print("‼ Nie znaleziono podanego przekierowania dla węzła " + name + "!");
            }
        }
        private void ForwardResponse(ArgList args)
        {
            if (args.Get("ok?") == "OK")
            {
                if (args.Get("info") == "ADDED")
                    manager.Print("☺ Dodano nowe przekierowanie w węźle " + name + "!");
                if (args.Get("info") == "OVERWRITTEN")
                    manager.Print("☺ Zmieniono przekierowanie w węźle " + name + "!");
            }
            else
            {
                if (args.Get("info") == "BADPORTS")
                    manager.Print("‼ Podano złe porty dla przekierowania w węźle " + name + "!");
            }
        }
        private void RemoveResponse(ArgList args)
        {
            if (args.Get("ok?") == "OK")
            {
                if (args.Get("info") == "REMOVED")
                    manager.Print("☺ Usunięto przekierowanie w węźle " + name + "!");
            }
            else
            {
                if (args.Get("info") == "NOENTRY")
                    manager.Print("‼ Nie znaleziono przekierowania w węźle " + name + "!");
            }
        }
        public bool GetIsClient() { return isClient; }

        public string DispName
        {
            get
            {
                if (alive)
                    return "♥ " + name;
                else
                    return "+ " + name;
            }
        }
        public string gName
        {
            get
            {
                return name;
            }
        }
        public string uName
        {
            get
            {
                return name.ToUpper();
            }
        }

        //ODCZYTYWANIE POWIADOMIEŃ
        private void ConfirmedKeepAlive(ArgList args)
        {
            if (args.Get("name") == name)
            {
                KeepAlive(args);
            }
            else
            {
                manager.Print("‼ Agent węzła " + args.Get("name") + " nadaje do portu węzła " + name + "!");
            }
        }
        private void KeepAlive(ArgList args)
        {
            if (!alive)
            {
                alive = true;
                manager.NodeIsBorn(this);
            }
            else
            {
                manager.Print("☺ Węzeł " + name + " twierdzi, że żyje!");
            }
            timeout = 60;
        }
        public void CountDown()
        {
            timeout -= 1;
            if (timeout == 0)
            {
                alive = false;
                manager.NodeIsKilled(this, true);
            }
        }

    }
    }


